package com.netty.esc.service.http.impl;

import com.netty.esc.Interceptor.annotation.FuckAll;
import com.netty.esc.common.enums.RelationShipEnum;
import com.netty.esc.common.enums.UserImKeyEnum;
import com.netty.esc.common.global.UserCache;
import com.netty.esc.dao.mapper.FriendMapper;
import com.netty.esc.dao.mapper.UserMapper;
import com.netty.esc.dao.repository.UserRepository;
import com.netty.esc.entity.model.FriendsOnlineDTO;
import com.netty.esc.entity.model.TUserFriendsDTO;
import com.netty.esc.entity.neo4j.node.PersonEntity;
import com.netty.esc.entity.pojo.UserFriends;
import com.netty.esc.entity.pojo.UserInfo;
import com.netty.esc.service.isolate.CommonFriendService;
import com.netty.esc.service.http.FriendService;
import lombok.SneakyThrows;
import org.neo4j.driver.internal.value.RelationshipValue;
import org.springframework.beans.BeanUtils;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SessionCallback;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author: ext.zhaoyang17
 * @date: 2022.10.21
 */
@Service
@FuckAll
public class FriendServiceImpl implements FriendService {

    @Resource
    FriendMapper friendMapper;
    @Resource
    RedisTemplate<String,Object> redisTemplate;

    @Resource
    UserRepository userRepository;

    @Resource
    UserMapper userMapper;

    @Resource
    CommonFriendService commonFriendService;



    @Override
    public List<TUserFriendsDTO> findFriendList(Long userId) {
        List<PersonEntity> personEntities = userRepository.findFriendRelation(userId).orElse(new ArrayList<>());
        if (personEntities.isEmpty()){
            return new ArrayList<>();
        }
        List<Long> collect = personEntities.stream().map(PersonEntity::getId).collect(Collectors.toList());
        List<TUserFriendsDTO> userInfoList = userMapper.findUserInfoList(collect);
        return userInfoList;
    }

    @Override
    public List<FriendsOnlineDTO> findFriendOnlines() {
        UserInfo currentUser = UserCache.getCurrentUser();
        Long id = currentUser.getId();
        List<Long> userList = userRepository.findFriendRelation(id).map(e -> e.stream().map(PersonEntity::getId).collect(Collectors.toList())).orElse(new ArrayList<>());
        if (userList.isEmpty()){
            return null;
        }
        List<FriendsOnlineDTO> friendsOnlineDTOList = new ArrayList<>();
        //开启管道进行批量查询
        List<Object> pipelined = redisTemplate.executePipelined(new SessionCallback<FriendsOnlineDTO>() {
            @Override
            public <K, V> FriendsOnlineDTO execute(RedisOperations<K, V> operations) throws DataAccessException {
                RedisTemplate<String, Long> redisTemplate1 = (RedisTemplate<String, Long>) operations;
                for (Long userId : userList) {
                    redisTemplate1.opsForValue().getBit(UserImKeyEnum.ESC_IM_ONLINE.getKey(), userId);
                }
                return null;
            }
        });
        //循环组装数据,感觉应该有能优化的点
        for (int i = 0; i < userList.size(); i++) {
            Long userId = userList.get(i);
            Boolean online = (boolean)pipelined.get(i);
            FriendsOnlineDTO friendsOnlineDTO = new FriendsOnlineDTO();
            friendsOnlineDTO.setOnline(online);
            friendsOnlineDTO.setUserId(userId);
            friendsOnlineDTOList.add(friendsOnlineDTO);
        }
        return friendsOnlineDTOList;
    }

    @Override
    public UserInfo findFriend(String telephone) {
        UserInfo friend = userMapper.findUser(telephone);
        friend.setId(null);
        friend.setCreateTime(null);
        friend.setPassword(null);
        return friend;
    }

    @Override
    @SneakyThrows
    public void addFriend(Long userId, String telephone, String message) {
        UserInfo friend = userMapper.findUser(telephone);

        //加入判断，如果用户添加自己好友，发送异常
        if (Objects.equals(userId, friend.getId())){
            throw new RuntimeException("用户禁止添加自己为好友！");
        }
        Optional<List<Object>> user1DirectionRelationShipByUndirected = userRepository.find2User1DirectionRelationShipByUndirected(userId, friend.getId());
        boolean b = judgeUserOneDirectionRelationShipAlreadyExists(user1DirectionRelationShipByUndirected, RelationShipEnum.FRIEND);
        if (b){
            throw new RuntimeException("两者已经是好友了");
        }
        UserFriends userFriends = new UserFriends();
        userFriends.setId(userId);
        userFriends.setIsRead(0);
        userFriends.setMessage(message);
        //好友请求保留三十天响应，如果超过三十天则请求失效目前看redis不支持对hash内的key做失效时间转换
        redisTemplate.opsForHash().put(UserImKeyEnum.ESC_IM_ADD_FRIEND.getKey() + ":" + friend.getId(), userId.toString(), userFriends);
        redisTemplate.expire(UserImKeyEnum.ESC_IM_ADD_FRIEND.getKey() + ":" + friend.getId(),30, TimeUnit.DAYS);
        assentFinishSendMessage(userId, friend.getId());
    }

    @Override
    public List<UserFriends> friendRequestList() {
        List<UserFriends> userFriendsList = new ArrayList<>();
        UserInfo currentUser = UserCache.getCurrentUser();
        Long userId = currentUser.getId();
        Map<Object, Object> applyFriendsList = redisTemplate.opsForHash().entries(UserImKeyEnum.ESC_IM_ADD_FRIEND.getKey() + ":" + userId);
        applyFriendsList.forEach((k,v)->{
            UserFriends userFriends = (UserFriends)v;
            Long friendId = Long.valueOf((String) k);
            UserInfo userInfoById = userMapper.findUserInfoByIdForFriend(friendId);
            if (userInfoById==null){
                return;
            }
            UserFriends userFriend = new UserFriends();
            BeanUtils.copyProperties(userInfoById,userFriend);
            userFriend.setIsRead(userFriends.getIsRead());
            userFriend.setMessage(userFriends.getMessage());
            userFriendsList.add(userFriend);
            //todo: 查看好友请求列表要不要改造成根据点进去看详情后再取消，后续待定
            userFriends.setIsRead(1);
        });
        redisTemplate.opsForHash().putAll(UserImKeyEnum.ESC_IM_ADD_FRIEND.getKey() + ":" + userId,applyFriendsList);
        redisTemplate.expire(UserImKeyEnum.ESC_IM_ADD_FRIEND.getKey() + ":" + userId,30, TimeUnit.DAYS);
        return userFriendsList;
    }

    @Override
    @SneakyThrows
    @Transactional(rollbackFor = Exception.class)
    public void assentApplyFriend(Long userId, String telephone) {
        //获取同意的好友信息
        UserInfo friend = userMapper.findUser(telephone);
        if (friend==null)return;
        //从redis中拉取所有好友请求列表
        Map<Object, Object> applyFriendsList = redisTemplate.opsForHash().entries(UserImKeyEnum.ESC_IM_ADD_FRIEND.getKey() + ":" + userId);
        UserFriends userFriends = (UserFriends) applyFriendsList.get(friend.getId().toString());
        //如果查询不到对应的请求信息，则视为无效同意
        if (userFriends==null)return;
        //创建好友关系
        userRepository.createFriendRelationShip(friend.getId(), userId, RelationShipEnum.FRIEND.getType());
        applyFriendsList.remove(friend.getId());
        redisTemplate.opsForHash().putAll(UserImKeyEnum.ESC_IM_ADD_FRIEND.getKey() + ":" + userId,applyFriendsList);
        redisTemplate.expire(UserImKeyEnum.ESC_IM_ADD_FRIEND.getKey() + ":" + userId,30, TimeUnit.DAYS);

        //todo:给好友发消息,版本1.1制作
    }

    @Override
    @SneakyThrows
    public void pushOneToBlacklist(String telephone) {
        UserInfo target = userMapper.findUser(telephone);
        Long me = UserCache.getCurrentUser().getId();
        //加入判断，如果用户添加自己好友，发送异常
        if (Objects.equals(me, target.getId())){
            throw new Exception("用户禁止拉黑自己！");
        }
        //拉黑
        this.userRepository.createBlacklistRelationShip(target.getId(), me);
    }

    /**
     *
     * 添加完成好友后给对应好友发送信息，并且触发好友刷新好友列表
     */
    private void assentFinishSendMessage(Long userId,Long friendId){
        Optional.ofNullable(redisTemplate.opsForValue().getBit(UserImKeyEnum.ESC_IM_ONLINE.getKey(), friendId))
                .ifPresent(e->{ if (e){commonFriendService.assentFinishSendMessage(userId);}});
    }

    /**
     * @param data 关系start Node
     * @param relationShipEnum 关系 end  Node
     * @return 是否已存在该关系
     */
    private boolean judgeUserOneDirectionRelationShipAlreadyExists(Optional<List<Object>> data,RelationShipEnum relationShipEnum){
        return data.map(e-> e.stream()
                        .map(ship -> ((RelationshipValue) ship).asEntity())
                        .map(relationship->{
                            //判断两者是否有关系
                            if (relationShipEnum.getType().equals(relationship.type())){
                                    return relationship.type();
                            }else {
                                //无此关系，匹配失败
                                return "";
                            }
                        }).anyMatch(a -> a.equals(relationShipEnum.getType())))
                .orElse(false);

    }



}
